'''
【基础函数·文本搜索·提取关键词】
这个文件主要存放功能函数
findZodiacWord(search_string, kw, as_text=True, strict_mode=False)
- 给定文本，通过正则表达式re，提取干支时间信息（中文简体繁体都一样，除非特地把丑写成醜）
- search_string：待查找的文本
- kw：后缀词汇，关键字，一般是年月日时
- as_text：是否输出字符串，否则输出list[int]
- strict_mode：当strict_mode为True时，正则表达式要求年份必须同时包含一个天干和一个地支，而且它们之间不能有其他字符；当strict_mode为False时，正则表达式的天干部分变为可选的（通过在天干组后面加上问号 ?）。这意味着可以只匹配到地支部分，或者同时匹配到天干和地支

identify_GuaName(s, Exact_match=False, as_text=True, full_match=False)
- s：待查找的文本
- Exact_match：当Exact_match为True时，要求查找六十四卦象的全名，比如乾为天，天风姤等等，当Exact_match为False时，只要是乾，否，泰，需，的字符一律视为卦象的名称，有可能造成误判。字符串含有乾宫，兑宫，这类字眼一般会被删除
- as_text：是否输出字符串，否则输出list[int]
- full_match：当full_match为True，匹配到所有的卦象名，全部会输出；当full_match为False，匹配到3个以上的卦象名，只会输出最相邻的2个，一般作为主卦变卦。
'''
import re
from constants import GAN, ZHI

"""提取干支时间"""


def findZodiacWord(search_string, kw, as_text=True, strict_mode=False):
    if strict_mode:
        pattern = f'(?P<year>(?P<gan>[甲乙丙丁戊己庚辛壬癸])(?P<zhi>[子丑寅卯辰巳午未申酉戌亥]){kw})'
    else:
        pattern = f'(?P<year>(?P<gan>[甲乙丙丁戊己庚辛壬癸])?(?P<zhi>[子丑寅卯辰巳午未申酉戌亥]){kw})'
    match = re.search(pattern, search_string)

    if match:
        gan = match.group('gan') if match.group('gan') else ''
        zhi = match.group('zhi')
        if as_text:
            return gan + zhi + kw
        else:
            return [GAN.index(gan) if gan else gan, ZHI.index(zhi) if zhi else zhi]
    else:
        return '' if as_text else [None, None]


def extract_ganzhi(text, kw=None, as_text=False):
    dct = {
        '年': ('yg', 'y'), '月': ('mg', 'm'), '日': ('dg', 'd'), '时': ('hg', 'h')
    }
    if kw not in dct:
        raise ValueError("Invalid keyword")
    # 定义gan和zhi的正则表达式部分，它们是可选的；kw in 年月日时
    pattern = f"(?P<gan>[甲乙丙丁戊己庚辛壬癸]?)(?P<zhi>[子丑寅卯辰巳午未申酉戌亥]?){kw}"

    # 查找匹配
    match = re.search(pattern, text)

    if match:
        gan, zhi = match.groups()
        gan_name, zhi_name = dct[kw]
        if as_text:
            return {gan_name: gan, zhi_name: zhi}
        else:
            return {gan_name: GAN.index(gan) if gan else None, zhi_name: ZHI.index(zhi) if zhi else None}
    else:
        return {}


def extract_time_from_text(text, args):
    # time_line = text.split('\n')[1] if '\n' in text else text
    time_line = text
    return [findZodiacWord(time_line, tag) for tag in args]


'''
写一个正则表达式来匹配，分三部分组成，干+支+年|月|日|时，GAN = ["癸", "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬"]
ZHI = ["亥", "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌"]
'''


def extract_int_from_text(text, strict_mode=True):
    '''
    :param text: 待识别文本
    :return:长度为6的列表，分别是 年干 年支 月支 日干 日支 时辰
    '''
    # time_line = text.split('\n')[1] if '\n' in text else text
    time_line = text
    _1, _2 = findZodiacWord(time_line, '年', False)
    _3, _4 = findZodiacWord(time_line, '月', False)
    _5, _6 = findZodiacWord(time_line, '日', False, strict_mode=strict_mode)
    _7, _8 = findZodiacWord(time_line, '时', False)

    return {'yg': _1, 'y': _2, 'mg': _3, 'm': _4, 'dg': _5, 'd': _6, 'hg': _7, 'h': _8}


'''提取卦名'''


def filter_indices(li, start_idx):
    """
    根据给定的起始索引过滤元素。

    本函数的目标是筛选出列表中索引与起始索引之差在特定范围内的元素。
    这有助于从列表中剔除不满足索引条件的元素，聚焦于感兴趣的索引范围。

    参数:
    li: 一个包含索引和卦名元组的列表。
    start_idx: 起始索引值，用于计算索引差。

    返回:
    返回一个新的列表，仅包含索引与起始索引之差在1到19之间的元素。
    如果没有元素满足条件，则返回原始列表。
    """
    # 筛选出满足条件的元素
    temp_list = [(idx, gua_name) for idx, gua_name in li if 0 < idx - start_idx < 20]
    # 如果temp_list为空，则返回原始列表li
    return temp_list if temp_list else li



def find_2char_index(text, search_dic=None):
    # 检查是否有卦象的繁体字/模糊字。遍历字典，繁体替换为简体
    if search_dic is None:
        search_dic = GUA_NAME

    replace_map = {
        "離": "离", "濟": "济", "訟": "讼", "損": "损", "師": "师", "豐": "丰", "節": "节", "晉": "晋", "剝": "剥",
        "恆": "恒", "觀": "观", "過": "过", "隨": "随", "蟲": "蛊", "蠱": "蛊", "頤": "颐", "復": "复", "臨": "临",
        "賁": "贲", "無妄": "无妄", "謙": "谦", "渙": "涣", "漸": "渐", "歸": "归", "蓄": "畜", "遯": "遁",
        "為火": "为火", "大壯": "大壮", "為水": "为水", "為雷": "为雷", "兌為": "兑为", "為天": "为天", "為地": "为地",
        "為風": "为风", "為山": "为山", "風": "风", "澤": "泽", '曰': "日", "天风□": "天风姤", "天山□": "天山遁",
        "泽天□": "泽天夬", "水山赛": "水山蹇", "暌": "睽",
    }
    for old_word, new_word in replace_map.items():
        text = text.replace(old_word, new_word)

    # 遍历卦名g，在字符串s找到查找【卦名g】的【索引位置i】。(i,g)保存在列表s_idxes中。按照i排序，key=ele[0]

    # 只能找到出现一次的，出现两次以上的被忽略了。
    # s_idxes = [(s.find(g), g) for g in search_dic if s.find(g) != -1]

    # 能找到出现两次以上的：
    def find_all_occurrences(search_string, matching_name):
        s_idxes = []
        start_idx = 0

        # 死循环search字符串search_string所有的matching_name
        while True:

            # 指定了字符串s找字符g的起始索引：start_idx；找不到就退出循环
            idx = search_string.find(matching_name, start_idx)
            if idx == -1:
                break

            # 准备查找string的下一个matching_name，update起始索引start_idx
            start_idx = idx + len(matching_name)

            # 八卦名+宫的string不算进去；防止出现：乾宫/坎宫被算进去
            try:
                if search_string[start_idx] != "宫":
                    s_idxes.append((idx, matching_name))
            except IndexError:
                pass

        return s_idxes

    s_idxes = []
    for g in search_dic:
        s_idxes.extend(find_all_occurrences(text, g))

    s_idxes.sort(key=lambda x: x[0])

    start_idx = text.find('得')
    if start_idx != -1 and len(s_idxes) >= 2:
        s_idxes = filter_indices(s_idxes, start_idx)

    return s_idxes


def identify_GuaName(s, Exact_match=False, as_text=True, full_match=False):
    """
    识别卦名并返回其在卦名列表中的索引。

    参数:
    s - 输入字符串，可能包含卦名。
    Exact_match - 是否需要精确匹配卦名，默认为False。
    as_text - 是否将结果以文本形式返回，默认为True。
    full_match - 是否需要匹配所有找到的卦名，默认为False。

    返回:
    如果as_text为True，则返回以文本形式表示的卦名索引列表。
    否则，返回卦名索引列表。
    """
    from constants import GUA_NAME, GUA_NAMES
    # GuaName_li[idx]为元组(s_idx,GuaName)

    # 能否的bug，识别出否卦来了
    s = s.replace("能否", "").replace("是否", "").replace("成否", "")
    s = s + '助'

    refer_lst = GUA_NAME
    if Exact_match:
        refer_lst = GUA_NAMES

    GuaName_li = find_2char_index(s, search_dic=refer_lst)

    find_out = []
    if (length := len(GuaName_li)) == 1:
        # 卦名GuaName_li[0][1]在 卦名列表guaname 中的序号
        find_out = [refer_lst.index(GuaName_li[0][1])]
    elif length == 2:
        find_out = list(map(lambda x: refer_lst.index(x), (GuaName_li[0][1], GuaName_li[1][
            1])))  # find_out = [refer_lst.index(GuaName_li[0][1]),refer_lst.index(GuaName_li[1][1])]

    elif length >= 3 and not full_match:
        # 找到相邻差距最小的两个数
        neibor_least_num = []
        mindiff = 100
        for i in range(len(GuaName_li) - 1):
            diff = GuaName_li[i + 1][0] - GuaName_li[i][0]
            if diff < mindiff:
                mindiff = diff
                neibor_least_num = [GuaName_li[i], GuaName_li[i + 1]]  # 此为元组
        find_out = list(map(lambda x: refer_lst.index(x), (neibor_least_num[0][1], neibor_least_num[1][1])))
    elif full_match:
        find_out = list(map(lambda x: refer_lst.index(x), [li[1] for li in GuaName_li]))
    if as_text:
        return ' '.join([str(n) for n in find_out])
    else:
        return find_out



if __name__ == '__main__':
    texts = ['甲辰年', '午年', '未年', '乙年']
    for text in texts:
        print(extract_ganzhi(text, '年', True))
